#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cassert>
#include <cstdlib>
#include <pthread.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
//包含unix头文件 fork
#include "util.hpp"
using namespace std;

namespace server
{
    enum {USAGE_ERR = 1, SOCKET_ERR, BIND_ERR, LISTEN_ERR};

    static const uint16_t gport = 8080;
    static const int gbacklog = 5;
    class TcpServer
    {
    public:
        TcpServer(const uint16_t &port = gport)
            :_listenSockfd(-1), _port(port)
        {}
        void initServer()
        {
            // 1.创建TCP套接字
            _listenSockfd = socket(AF_INET, SOCK_STREAM, 0);
            if(_listenSockfd == -1) exit(SOCKET_ERR);
            int opt = 1;
            setsockopt(_listenSockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
            // 2.bind绑定自己的网络信息, ip+port
            struct sockaddr_in local; // 服务器本地的网络信息
            memset(&local, 0, sizeof(local));
            local.sin_family = PF_INET; // IP protocol family
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY; // 任意ip
            if(bind(_listenSockfd, (struct sockaddr *)&local, sizeof(local)) == -1) exit(BIND_ERR);
            // 3.设置服务器的socket为监听状态 --> 监听什么时候客户端发来连接请求
            if(listen(_listenSockfd, gbacklog) == -1) exit(LISTEN_ERR);
        }
        void start()
        {               
            for(;;)//服务器启动后要进行死循环
            {
                // 4.server和client要建立连接, server获取来自client的连接请求
                struct sockaddr_in peer; //远端的客户端信息
                socklen_t len = sizeof(peer);
                int sockfd = accept(_listenSockfd, (struct sockaddr *)&peer, &len);//sockfd是未来和client进行通信的套接字
                if(sockfd == -1) continue;

                pthread_t tid;
                
                pthread_create(&tid, NULL, entrance, &sockfd);
            }
        }
        ~TcpServer(){}
    private:
        static void create_file(int sockfd)
        {
            //1.接收文件名 + 创建文件
            char buffer[1024];
            int n = read(sockfd, buffer, 1024);

            string filename(buffer, n);
            filename.insert(0, "./serverLocal/");

            cout << "提示信息: "<< filename << endl;

            file_util::write(filename, "");
            //2.接收文件大小
            int size = 0;
            n = read(sockfd, &size, 4);
            cout << "提示信息: "<< size << endl;
            string body;
            body.resize(size);
            
            //3.读取文件内容+向文件进行写入
            n = read(sockfd, (void*)body.c_str(), size);//期望读到的字节数就是size的大小
            file_util::write(filename, body);
        }
        static void return_file(int sockfd)
        {
            //1.接收文件名
            string filename;
            filename.resize(1024);
            int n = read(sockfd, (void*)filename.c_str(), filename.size());
            cout << filename << endl;
            //2.发送文件大小
            string body;
            file_util::read(filename, body);
            
            cout << body << endl;
            int size = body.size();
            write(sockfd, &size, sizeof(size));

            cout << body.size() << endl;
            //3.发送文件内容
            write(sockfd, body.c_str(), body.size());
        }
        static void* entrance(void* sockfd)
        {
            while (true)
            {
                int _sockfd = *(int*)sockfd;
                char buffer[1024];
                size_t n = read(_sockfd, buffer, sizeof(buffer) - 1);
                int32_t* valuePtr = reinterpret_cast<int32_t*>(buffer);
                cout << "读到的功能选项为: " << *valuePtr << endl;
                if(*valuePtr == 1)
                {
                    create_file(_sockfd);
                }
                else if(*valuePtr == 2)
                {
                    return_file(_sockfd);
                }
            }
        }
    private:
        int _listenSockfd; //核心作用不是用来通信的, 而是用来监听客户端是否有连接请求的
        uint16_t _port;
    };

} // namespace server